/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.facebook.fresco.helper.config;

import ohos.app.Context;
import ohos.media.image.common.PixelFormat;

import com.facebook.cache.disk.DiskCacheConfig;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.internal.Supplier;
import com.facebook.common.memory.MemoryTrimType;
import com.facebook.common.memory.MemoryTrimmable;
import com.facebook.common.memory.MemoryTrimmableRegistry;
import com.facebook.common.memory.NoOpMemoryTrimmableRegistry;
import com.facebook.common.util.ByteConstants;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.fresco.helper.utils.MLog;
import com.facebook.imagepipeline.cache.MemoryCacheParams;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.listener.RequestListener;
import com.facebook.imagepipeline.listener.RequestLoggingListener;
import com.facebook.imagepipeline.producers.HttpUrlConnectionNetworkFetcher;
import com.facebook.imagepipeline.producers.NetworkFetcher;

import java.io.File;
import java.util.HashSet;
import java.util.Set;

/**
 * 凤凰配置
 *
 * @author dev
 * @since 2021-07-29
 */
public class PhoenixConfig {
    private PhoenixConfig() {
    }

    /**
     * 得到
     *
     * @param context 上下文
     * @return {@link ImagePipelineConfig}
     */
    public static ImagePipelineConfig get(Context context) {
        return new PhoenixConfig.Builder(context).build();
    }

    /**
     * 构建器
     *
     * @author dev
     * @since 2021-07-29
     */
    public static class Builder implements MemoryTrimmable {
        private static final String IMAGE_PIPELINE_CACHE_DIR = "image_cache";
        private static final String IMAGE_PIPELINE_SMALL_CACHE_DIR = "image_small_cache";
        private static final int MAX_DISK_SMALL_CACHE_SIZE = 10 * ByteConstants.MB;
        private static final int MAX_DISK_SMALL_ONLOWDISKSPACE_CACHE_SIZE = 5 * ByteConstants.MB;

        private final Context mContext;
        private Set<RequestListener> mRequestListeners;
        private MemoryTrimmableRegistry mMemoryTrimmableRegistry;
        private Supplier<MemoryCacheParams> mBitmapMemoryCacheParamsSupplier;
        private DiskCacheConfig mMainDiskCacheConfig;
        private DiskCacheConfig mSmallImageDiskCacheConfig;
        private NetworkFetcher mNetworkFetcher;

        /**
         * 构建器
         *
         * @param context 上下文
         */
        public Builder(Context context) {
            mContext = Preconditions.checkNotNull(context);
        }

        /**
         * 设置网络访问者
         *
         * @param networkFetcher 网络访问者
         * @return {@link Builder}
         */
        public Builder setNetworkFetcher(NetworkFetcher networkFetcher) {
            mNetworkFetcher = networkFetcher;
            return this;
        }

        /**
         * 设置请求听众
         *
         * @param requestListeners 请求听众
         * @return {@link Builder}
         */
        public Builder setRequestListeners(Set<RequestListener> requestListeners) {
            mRequestListeners = requestListeners;
            return this;
        }

        /**
         * 设置内存trimmable注册表
         *
         * @param memoryTrimmableRegistry 内存trimmable注册表
         * @return {@link Builder}
         */
        public Builder setMemoryTrimmableRegistry(MemoryTrimmableRegistry memoryTrimmableRegistry) {
            mMemoryTrimmableRegistry = memoryTrimmableRegistry;
            return this;
        }

        /**
         * 设置位图内存缓存参数供应商
         *
         * @param bitmapMemoryCacheParamsSupplier 位图内存缓存参数供应商
         * @return {@link Builder}
         */
        public Builder setBitmapMemoryCacheParamsSupplier(
            Supplier<MemoryCacheParams> bitmapMemoryCacheParamsSupplier) {
            mBitmapMemoryCacheParamsSupplier = bitmapMemoryCacheParamsSupplier;
            return this;
        }

        /**
         * 设置主磁盘缓存配置
         *
         * @param mainDiskCacheConfig 主要磁盘高速缓存配置
         * @return {@link Builder}
         */
        public Builder setMainDiskCacheConfig(DiskCacheConfig mainDiskCacheConfig) {
            mMainDiskCacheConfig = mainDiskCacheConfig;
            return this;
        }

        /**
         * 磁盘高速缓存配置设置小图像
         *
         * @param smallImageDiskCacheConfig 磁盘高速缓存配置小图像
         * @return {@link Builder}
         */
        public Builder setSmallImageDiskCacheConfig(DiskCacheConfig smallImageDiskCacheConfig) {
            mSmallImageDiskCacheConfig = smallImageDiskCacheConfig;
            return this;
        }

        /**
         * 构建
         *
         * @return {@link ImagePipelineConfig}
         */
        public ImagePipelineConfig build() {
            if (mNetworkFetcher == null) {
                mNetworkFetcher = new HttpUrlConnectionNetworkFetcher();
            }

            if (mRequestListeners == null) {
                mRequestListeners = new HashSet<>();
                mRequestListeners.add(new RequestLoggingListener());
            }

            if (mBitmapMemoryCacheParamsSupplier == null) {
                mBitmapMemoryCacheParamsSupplier = new BitmapMemoryCacheParamsSupplier(mContext.getAbilityManager());
            }

            createMemoryTrimmableRegistry();

            /*
             * 推荐缓存到应用本身的缓存文件夹，这么做的好处是:
             * 1、当应用被用户卸载后能自动清除缓存
             * 2、一些内存清理软件可以扫描出来，进行内存的清理
             */
            if (mMainDiskCacheConfig == null) {
                File fileCacheDir = mContext.getCacheDir();
                mMainDiskCacheConfig = DiskCacheConfig.newBuilder(mContext)
                    .setBaseDirectoryName(IMAGE_PIPELINE_CACHE_DIR)
                    .setBaseDirectoryPath(fileCacheDir)
                    .build();
            }

            if (mSmallImageDiskCacheConfig == null) {
                File fileCacheDir = mContext.getCacheDir();
                mSmallImageDiskCacheConfig = DiskCacheConfig.newBuilder(mContext)
                    .setBaseDirectoryPath(fileCacheDir)
                    .setBaseDirectoryName(IMAGE_PIPELINE_SMALL_CACHE_DIR)
                    .setMaxCacheSize(MAX_DISK_SMALL_CACHE_SIZE)
                    .setMaxCacheSizeOnLowDiskSpace(MAX_DISK_SMALL_ONLOWDISKSPACE_CACHE_SIZE)
                    .build();
            }
            return ImagePipelineConfig.newBuilder(mContext)
                .setBitmapsConfig(PixelFormat.ARGB_8888) // 若不是要求忒高清显示应用，就用使用RGB_565吧（默认是ARGB_8888)
                .setDownsampleEnabled(true) // 在解码时改变图片的大小，支持PNG、JPG以及WEBP格式的图片，与ResizeOptions配合使用
                .setNetworkFetcher(mNetworkFetcher)
                .setRequestListeners(mRequestListeners)
                .setMemoryTrimmableRegistry(mMemoryTrimmableRegistry) // 报内存警告时的监听
                .setBitmapMemoryCacheParamsSupplier(mBitmapMemoryCacheParamsSupplier) // 设置内存配置
                .setMainDiskCacheConfig(mMainDiskCacheConfig) // 设置主磁盘配置
                .setSmallImageDiskCacheConfig(mSmallImageDiskCacheConfig) // 设置小图的磁盘配置
                .build();
        }

        private void createMemoryTrimmableRegistry() {
            if (mMemoryTrimmableRegistry == null) {
                // 当内存紧张时采取的措施
                mMemoryTrimmableRegistry = NoOpMemoryTrimmableRegistry.getInstance();
                mMemoryTrimmableRegistry.registerMemoryTrimmable(this);
            }
        }

        @Override
        public void trim(MemoryTrimType trimType) {
            final double suggestedTrimRatio = trimType.getSuggestedTrimRatio();
            MLog.info("Fresco onCreate suggestedTrimRatio = " + suggestedTrimRatio);

            if (MemoryTrimType.OnCloseToDalvikHeapLimit.getSuggestedTrimRatio() == suggestedTrimRatio
                || MemoryTrimType.OnSystemLowMemoryWhileAppInBackground.getSuggestedTrimRatio()
                == suggestedTrimRatio
                || MemoryTrimType.OnSystemLowMemoryWhileAppInForeground.getSuggestedTrimRatio()
                == suggestedTrimRatio) {
                // 清除内存缓存
                Fresco.getImagePipeline().clearMemoryCaches();
            }
        }
    }
}
